﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using Microsoft.Win32;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.VisualBasic.FileIO;
using System.ComponentModel;

namespace BuildRunner
{
	/// <summary>
	/// MainWindow.xaml の相互作用ロジック
	/// </summary>
	public partial class MainWindow : Window
	{
		static TextBox m_staticEdit = null;
		static OpeCompile m_oc = null;

		ObservableCollection<SettingItem> m_ltSetting = new ObservableCollection<SettingItem>();
		List<Operation> m_ltOpe = null;
		int m_iRunIndex = 0;
		public MainWindow()
		{
			InitializeComponent();
		}

		private void Window_Loaded( object sender, RoutedEventArgs e )
		{
			lbSetting.ItemsSource = m_ltSetting;
		}

		private void beSelectFile_Click( object sender, RoutedEventArgs e )
		{
			//if( lbSetting.SelectedItem == null )
			//    return;

			//OpenFileDialog dlg = new OpenFileDialog();
			//var setting = lbSetting.SelectedItem as SettingItem;
			//if( setting.Target == "VS2010" ) {
			//    dlg.Filter = "VS2010(*.sln)|*.sln|VS2010(*.sln)|*.sln|VS2010 C++(*.vcxproj)|*.vcxproj";
			//} else if( setting.Target == "VC6++" ) {
			//    dlg.Filter = "VC++6.0(*.dsw)|*.dsw";
			//} else {
			//    dlg.Filter = "VS2010(*.sln)|*.sln|VS2010(*.sln)|*.sln|VS2010 C++(*.vcxproj)|*.vcxproj|VC++6.0(*.dsw)|*.dsw";
			//}
			//if( dlg.ShowDialog() == true ) 
			//    setting.FilePath = dlg.FileName;

			//lbSetting.ItemsSource = null;
			//lbSetting.ItemsSource = m_ltSetting;
		}

		private void btRun_Click( object sender, RoutedEventArgs e )
		{
			lbSetting.Visibility = System.Windows.Visibility.Collapsed;
			lbOperaiton.Visibility = System.Windows.Visibility.Visible;

			var parent = VisualTreeHelper.GetParent( ( sender as DependencyObject ) );
			var parentItem = VisualTreeHelper.GetParent( parent );
			var parentItem2 = VisualTreeHelper.GetParent( parentItem );


			//	コピー設定
			Dictionary<string,string> dicCopy = new Dictionary<string, string>();
			Regex regCopy = new Regex( "\\*(?<param>.*?)\\*");
			foreach( var item in m_ltSetting ) {
				foreach( var copy in item.ltCopy ) {
					var matches = regCopy.Matches( copy.PathTo );
					foreach( Match mt in matches ) {
						if( !dicCopy.ContainsKey( mt.Groups["param"].Value ) )
							dicCopy.Add( mt.Groups["param"].Value, "" );
					}
				}
			}
			dlgParam dlg = new dlgParam() { Owner = this, Param = dicCopy };
			dlg.ShowDialog();

			textEditor.Clear();
			m_ltOpe = new List<Operation>();
			foreach( var item in m_ltSetting ) {
				m_ltOpe.Add( new OpeCompile()
				{
					FilePath = item.FilePath,
					Type = item.Type,
					SelectedBuild = item.SelectedBuild,
					SelectedSetting = item.SelectedSetting,
					Error = new ObservableCollection<string>(),
					Warning = new ObservableCollection<string>(),
				} );
				foreach( var copy in item.ltCopy ) {
					var copyClone = new CopyFile() { PathFrom = copy.PathFrom, PathTo = copy.PathTo };
					foreach( var copyParam in dicCopy )
						copyClone.PathTo = copyClone.PathTo.Replace( "*" + copyParam.Key + "*", copyParam.Value );
					m_ltOpe.Add( copyClone );
				}
				if( item.IsClean ) {
					m_ltOpe.Add( new OpeClean()
					{
						FilePath = item.FilePath,
						Type = item.Type,
						SelectedSetting = item.SelectedSetting,
					} );
				}
			}
			lbOperaiton.ItemsSource = null;
			lbOperaiton.ItemsSource = m_ltOpe;

			m_iRunIndex = 0;
			m_staticEdit = textEditor;
			Run();
		}

		void Run()
		{
			if( m_iRunIndex >= m_ltOpe.Count ) {
				//lbSetting.Visibility = System.Windows.Visibility.Visible;
				//lbOperaiton.Visibility = System.Windows.Visibility.Collapsed;
				return;
			}
			if( m_ltOpe[m_iRunIndex] == null )
				return;
			var setting = m_ltOpe[m_iRunIndex];


			if( setting is CopyFile ) {
				CopyOperation( setting );
				m_iRunIndex++;
				Run();
				return;
			}

			//Processオブジェクトを作成
			System.Diagnostics.Process p = new System.Diagnostics.Process();

			//出力とエラーをストリームに書き込むようにする
			p.StartInfo.UseShellExecute = false;
			p.StartInfo.RedirectStandardOutput = true;
			p.StartInfo.RedirectStandardError = true;
			//OutputDataReceivedとErrorDataReceivedイベントハンドラを追加
			p.OutputDataReceived += p_OutputDataReceived;
			p.ErrorDataReceived += p_ErrorDataReceived;
			p.Exited += p_Exited;
			switch( setting.Type ) {
				case CompilerType.VS2010:
					p.StartInfo.FileName = @"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.com";
					break;
				case CompilerType.VC6:
					p.StartInfo.FileName = @"C:\Program Files (x86)\Microsoft Visual Studio\Common\MSDev98\Bin\MSDEV.COM";
					break;
			}

			p.StartInfo.RedirectStandardInput = false;
			p.StartInfo.CreateNoWindow = true;
			if( setting is OpeCompile ) {
				var s = setting as OpeCompile;
				switch( setting.Type ) {
					case CompilerType.VS2010:
						p.StartInfo.Arguments = string.Format( "{0} /{1} \"{2}\"", s.FilePath, s.SelectedBuild, s.SelectedSetting );
						break;
					case CompilerType.VC6:
						p.StartInfo.Arguments = string.Format( "{0} /MAKE \"{1}\" ", s.FilePath, s.SelectedSetting )
							+ (( s.SelectedBuild == "Rebuild" ) ? "/REBUILD" : "" );
						break;
				}
				m_oc = s;
			}else if( setting is OpeClean ) {
				var s = setting as OpeClean;
				switch( setting.Type ) {
					case CompilerType.VS2010:
						p.StartInfo.Arguments = string.Format( "{0} /CLEAN \"{1}\"", s.FilePath, s.SelectedSetting );
						break;
					case CompilerType.VC6:
						p.StartInfo.Arguments = string.Format( "{0} /MAKE \"{1}\" /CLEAN", s.FilePath, s.SelectedSetting );
						break;
				}
			}

			textEditor.AppendText( string.Format( "---{0} {1}---\r\n\r\n", p.StartInfo.FileName, p.StartInfo.Arguments ) );
			p.EnableRaisingEvents = true;
			//	起動
			p.Start();
			setting.Status7 = ucWorking.eStatus.Busy;

			//	非同期で出力とエラーの読み取りを開始
			p.BeginOutputReadLine();
			p.BeginErrorReadLine();
		}



		//OutputDataReceivedイベントハンドラ
		//行が出力されるたびに呼び出される
		static void p_OutputDataReceived( object sender,
			System.Diagnostics.DataReceivedEventArgs e )
		{
			//出力された文字列を表示する
			//Console.WriteLine( e.Data );
			if( e.Data != null ) {
				m_staticEdit.Dispatcher.BeginInvoke(
					 new Action( () =>
					 {
						 m_staticEdit.AppendText( e.Data );
						 m_staticEdit.AppendText( Environment.NewLine );
						 m_staticEdit.ScrollToEnd();

						 //var setting = m_ltOpe[m_iRunIndex] as OpeCompile;

						 Regex regWarning = new Regex( "^.*?([0-9]*):\\swarning\\sC[0-9]*:\\s.*$" );
						 var matchW = regWarning.Match( e.Data );
						 if( matchW.Success ) {
							 m_oc.Warning.Add( e.Data );
						 }
						 Regex regError = new Regex( "^.*?([0-9]*)\\s*:\\sfatal error\\sC[0-9]*:\\s.*$" );
						 var matchE = regError.Match( e.Data );
						 if( matchE.Success ) {
							 m_oc.Error.Add( e.Data );
						 }

					 } )
				);
			}
		}

		//ErrorDataReceivedイベントハンドラ
		static void p_ErrorDataReceived( object sender,
			System.Diagnostics.DataReceivedEventArgs e )
		{
			//エラー出力された文字列を表示する
			Console.WriteLine( "ERR>{0}", e.Data );
		}

		private void CopyOperation( Operation setting )
		{
			setting.Status7 = ucWorking.eStatus.Busy;
			var copy = setting as CopyFile;
			try {
				if( copy.CopyType == CopyType.Copy )
					FileSystem.CopyFile( copy.PathFrom, copy.PathTo );
				else if( copy.CopyType == CopyType.Move )
					FileSystem.MoveFile( copy.PathFrom, copy.PathTo );

			}
			catch( FileNotFoundException ex ) {
				MessageBox.Show( string.Format( "{0}", ex.Message ), "FileNotFoundException", MessageBoxButton.OK, MessageBoxImage.Error );
			}
			catch( IOException ex ) {
				MessageBox.Show( string.Format( "{0}", ex.Message ), "IOException", MessageBoxButton.OK,  MessageBoxImage.Error );
			}
			setting.Status7 = ucWorking.eStatus.Comp;
			return;
		}
		void p_Exited( object sender, EventArgs e )
		{
			var p = sender as Process;

			Console.ReadLine();
			m_staticEdit.Dispatcher.BeginInvoke(
				 new Action( () =>
				 {
					 m_staticEdit.AppendText( string.Format( "----[終了]{0} * {1}", p.ExitCode, p.ExitTime ) );
					 m_staticEdit.AppendText( Environment.NewLine );
					 m_staticEdit.ScrollToEnd();
					 p.Close();
				 } )
			);
			
			m_oc.Status7 = ucWorking.eStatus.Comp;
			this.Dispatcher.BeginInvoke(
				new Action( () =>
				{
					m_iRunIndex++;
					Run();
				})
			);

		}

		private void lbSetting_PreviewDragOver( object sender, DragEventArgs e )
		{
			if( e.Data.GetData( DataFormats.FileDrop ) != null ) {
				e.Effects = DragDropEffects.Copy;
			} else {
				e.Effects = DragDropEffects.None;
			}

			e.Handled = true;
		}

		private void lbSetting_Drop( object sender, DragEventArgs e )
		{
			string[] files = e.Data.GetData( DataFormats.FileDrop ) as string[];
			foreach( var file in  files ) {
				if( System.IO.Path.GetExtension( file ).ToLower() == ".sln" ) {

					ReadSln( file );
				}
				if( System.IO.Path.GetExtension( file ).ToLower() == ".dsw" ) {
					ReadDsw( file );
				}
			}
		}

		private void ReadDsw( string file )
		{
			var items = VC6.Analyse( file );
			foreach( var item in items )
				m_ltSetting.Add( new SettingItem()
				{
					FilePath = item.FilePath,
					Type = item.Type,
					Setting = item.Setting,
					Data = item,
					ltCopy = new ObservableCollection<CopyFile>(),
				} );
		}

		private void ReadSln( string file )
		{
			var item = VS2010Sln.Analyse( file );
			m_ltSetting.Add( new SettingItem()
			{
				FilePath = file,
				Type = item.Type,
				Setting = item.Setting,
				Data = item,
				ltCopy = new ObservableCollection<CopyFile>(),
			} );
		}

		private void AddCopy_Click( object sender, RoutedEventArgs e )
		{
			var bt = sender as Button;
			var stackpanel = bt.Parent as FrameworkElement;
			var dockpanel = stackpanel.Parent as FrameworkElement;
			var data = dockpanel.DataContext as SettingItem;
			dlgCopy2 dlg  = new dlgCopy2() { Owner = this };
			dlg.ShowDialog();
			data.ltCopy.Add( new CopyFile() { PathFrom = dlg.tbFrom.Text, PathTo = dlg.tbTo.Text } );
		}

		private void btSave_Click( object sender, RoutedEventArgs e )
		{
			//保存先のファイル名
			string fileName = @"setting.xml";

			//XmlSerializerオブジェクトを作成
			//オブジェクトの型を指定する
			System.Xml.Serialization.XmlSerializer serializer =
			new System.Xml.Serialization.XmlSerializer( typeof( ObservableCollection<SettingItem> ) );
			//書き込むファイルを開く（UTF-8 BOM無し）
			System.IO.StreamWriter sw = new System.IO.StreamWriter(
				fileName, false, new System.Text.UTF8Encoding( false ) );
			//シリアル化し、XMLファイルに保存する
			serializer.Serialize( sw, m_ltSetting );
			//ファイルを閉じる
			sw.Close();
		}

		private void btLoad_Click( object sender, RoutedEventArgs e )
		{
			//保存元のファイル名
			string fileName = @"setting.xml";

			//XmlSerializerオブジェクトを作成
			System.Xml.Serialization.XmlSerializer serializer =
			new System.Xml.Serialization.XmlSerializer( typeof( ObservableCollection<SettingItem> ) );
			//読み込むファイルを開く
			System.IO.StreamReader sr = new System.IO.StreamReader(
				fileName, new System.Text.UTF8Encoding( false ) );
			//XMLファイルから読み込み、逆シリアル化する
			m_ltSetting = (ObservableCollection<SettingItem>)serializer.Deserialize( sr );
			//ファイルを閉じる
			sr.Close();

			lbSetting.ItemsSource = null;
			lbSetting.ItemsSource = m_ltSetting;
		}

		private void btJP_Click( object sender, RoutedEventArgs e )
		{
			ResourceService.Current.ChangeCulture( "ja-JP" );
			//Working.IsBusy = true;
		}

		private void btEng_Click( object sender, RoutedEventArgs e )
		{
			ResourceService.Current.ChangeCulture( "en-US" );
			//Working.IsBusy = false;
		}

		private void btSetting_Click( object sender, RoutedEventArgs e )
		{
			lbSetting.Visibility = System.Windows.Visibility.Visible;
			lbOperaiton.Visibility = System.Windows.Visibility.Collapsed;
		}

		private void btOperation_Click( object sender, RoutedEventArgs e )
		{
			lbSetting.Visibility = System.Windows.Visibility.Collapsed;
			lbOperaiton.Visibility = System.Windows.Visibility.Visible;
		}

		private void btAdd_Click( object sender, RoutedEventArgs e )
		{
			btAdd.ContextMenu.IsOpen = true;
		}

		private void test_MouseDoubleClick( object sender, MouseButtonEventArgs e )
		{
			if( !( sender is ListBox ) )
				return;
			var lb = sender as ListBox;
			if( !( lb.SelectedItem is CopyFile ) )
				return;
			var cf = lb.SelectedItem as CopyFile;
			dlgCopy2 dlg = new dlgCopy2 { Owner = this, DataContext = cf };
			dlg.ShowDialog();
			lb.Items.Refresh();
		}

		private void test_PreviewKeyDown( object sender, KeyEventArgs e )
		{
			if( !( sender is ListBox ) )
				return;
			var lb = sender as ListBox;
			if( !( lb.SelectedItem is CopyFile ) )
				return;
			if( MessageBox.Show( "削除しますか？", "削除しますか？", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No, MessageBoxOptions.DefaultDesktopOnly )
				== MessageBoxResult.No )
				return;
			( (ObservableCollection<CopyFile>)lb.ItemsSource ).Remove( lb.SelectedItem as CopyFile );
			lb.Items.Refresh();
		}

		private void lbSetting_PreviewKeyDown( object sender, KeyEventArgs e )
		{
			if( !( sender is ListBox ) )
				return;
			var lb = sender as ListBox;
			if( !( lb.SelectedItem is SettingItem ) )
				return;
			if( MessageBox.Show( "削除しますか？", "削除しますか？", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No, MessageBoxOptions.DefaultDesktopOnly )
				== MessageBoxResult.No )
				return;
			( (ObservableCollection<SettingItem>)lb.ItemsSource ).Remove( lb.SelectedItem as SettingItem );
			lb.Items.Refresh();
		}
	}
	
	[System.Xml.Serialization.XmlInclude( typeof( VS2010Sln ) )]
	[System.Xml.Serialization.XmlInclude( typeof( VC6 ) )]
	public class SettingItem
	{
		public string FilePath { get; set; }
		public CompilerType Type { get; set; }
		public List<string> Setting { get; set; }
		public object Data { get; set; }

		public string SelectedSetting { get; set; }
		public string SelectedBuild { get; set; }
		public bool IsClean { get; set; }
		public ObservableCollection<CopyFile> ltCopy { get; set; }
	}
	public enum CompilerType
	{
		VC6,
		VS2008,
		VS2010,
	}
	public enum CopyType
	{
		Copy,
		Move,
	}
	public class CopyFile : Operation
	{
		public CopyType CopyType { get; set; }
		public string PathFrom { get; set; }
		public string PathTo { get; set; }
	}


	public class Operation :  INotifyPropertyChanged
	{
		public string FilePath { get; set; }
		public CompilerType Type { get; set; }
		public string SelectedSetting { get; set; }

		ucWorking.eStatus m_eStatus = ucWorking.eStatus.Wait;
		public ucWorking.eStatus Status7 { get { return m_eStatus; }
			set
			{
				m_eStatus = value;
				NotifyPropertyChanged( "Status7" );
			}
		}

		#region INotifyPropertyChanged メンバー

		public event PropertyChangedEventHandler  PropertyChanged;
		protected void NotifyPropertyChanged( String info )
		{
			if( PropertyChanged != null ) {
				PropertyChanged( this, new PropertyChangedEventArgs( info ) );
			}
		}
		#endregion
	}
	public class OpeCompile : Operation
	{
		public string SelectedBuild { get; set; }
		public ObservableCollection<string> Warning { get; set; }
		public ObservableCollection<string> Error { get; set; }
	}
	public class OpeClean : Operation
	{
	}
}
